#include <iostream>
#include <ctime>
#include <vector>
#include <pthread.h>
#include <semaphore.h>
#define PLATESIZE 10
#define PRODUCERSIZE 5
#define PRODUCTION_NUM 10
#define CONSUMERSIZE 4

// 生产者互斥量，消费者互斥量，空位资源信号量，物品资源信号量
sem_t pmutex, cmutex, empty, full;
// 队尾指针，队头指针
int in = 0, out = 0;
// 盘子（缓冲区）
std::vector<int> plate(PLATESIZE);

void* producer(void* args)
{
    std::string* name = static_cast<std::string*>(args);
    for(int i = 0; i < PRODUCTION_NUM; i++)
    {
        int weight = rand() % 9 + 1;
        sem_wait(&empty);
        sem_wait(&pmutex);
        plate[in] = weight;
        in = (in + 1) % PLATESIZE;
        printf("[生产者%s]向盘中放入一个重%dkg的苹果\n", name->c_str(), weight);
        sem_post(&pmutex);
        sem_post(&full);
    }
    printf("[生产者%s]退出...\n", name->c_str());
    return nullptr;
}

void* consumer(void* args)
{
    std::string* name = static_cast<std::string*>(args);
    while(true)
    {
        sem_wait(&full);
        sem_wait(&cmutex);
        int weight = plate[out];
        if(weight == 0)
        {
            sem_post(&cmutex);
            break;
        }
        plate[out] = 0;
        out = (out + 1) % PLATESIZE;
        printf("[消费者%s]从盘中取出一个重%dkg的苹果\n", name->c_str(), weight);
        sem_post(&cmutex);
        sem_post(&empty);
    }
    printf("[消费者%s]退出...\n", name->c_str());
    return nullptr;
}

int main()
{
    srand((unsigned int)time(nullptr));
    // 值为1用作互斥量
    sem_init(&pmutex, 0, 1); 
    sem_init(&cmutex, 0, 1);
    // 初始时盘子全为空
    sem_init(&empty, 0, PLATESIZE);
    sem_init(&full, 0, 0);

    std::vector<std::pair<pthread_t, std::string>> producers(PRODUCERSIZE);
    for(int i = 0; i < PRODUCERSIZE; i++)
    {
        producers[i].second = "producer-" + std::to_string(i);
        pthread_create(&producers[i].first, nullptr, producer, &producers[i].second);
    }
    std::vector<std::pair<pthread_t, std::string>> consumers(CONSUMERSIZE);
    for(int i = 0; i < CONSUMERSIZE; i++)
    {
        consumers[i].second = "consumer-" + std::to_string(i);
        pthread_create(&consumers[i].first, nullptr, consumer, &consumers[i].second);
    }

    for(auto& pr : producers)
        pthread_join(pr.first, nullptr);

    for(int i = 0; i < CONSUMERSIZE; i++)
        sem_post(&full);        // 发送终止信号

    for(auto& pr : consumers)
        pthread_join(pr.first, nullptr);
    
    sem_destroy(&pmutex);
    sem_destroy(&cmutex);
    sem_destroy(&empty);
    sem_destroy(&full);
    return 0;
}